﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "GLBatchRender.h"

namespace UX_STL
{
namespace GLShell
{

/*
=================================================
	конструктор
=================================================
*/
	GLBatchRender::GLBatchRender():
		_fCurrentZ(0.f),		_fZStep(0.0001f),		_bScissor(false),
		_fDefZ(-1.f),			_bChanged(true),
		_uDefault2DBatch(0),	_uDefault3DBatch(0),	_uDefault2DGSBatch(0)
	{
	}

/*
=================================================
	деструктор
=================================================
*/
	GLBatchRender::~GLBatchRender()
	{
		_Destroy();
	}
	
/*
=================================================
	инициализация батч рендера
=================================================
*/
	bool GLBatchRender::Create(IVertexBuffer *pVB)
	{
		_Destroy();
		
		if ( pVB != null )
			_pVB = pVB;
		else {
			_pVB = &_cVB;
		}

		_fZStep		= 0.0001f;
		_fDefZ		= -1.f;
		_bChanged	= false;
		_bScissor	= false;
		_fCurrentZ	= _fDefZ;

		_pVB->Reserve( 1<<10 );
		return true;
	}
	
/*
=================================================
	установить индексы для стандартных
	типов батчей
=================================================
*/
	void GLBatchRender::SetDefaultBatches(usize uDefault2DBatch, usize uDefault2DGSBatch, usize uDefault3DBatch)
	{
		_uDefault2DBatch	= uDefault2DBatch;
		_uDefault3DBatch	= uDefault3DBatch;
		_uDefault2DGSBatch	= uDefault2DGSBatch;
	}

/*
=================================================
	добавить тип батча
=================================================
*/
	bool GLBatchRender::AddBatchType(const GLVerticesAttribs_t &aAttribs, uint8 uVertSize, usize &uIndex)
	{
		CHECK_ERR( !aAttribs.Empty() and uVertSize > 0 );

		GLBatch	c_batch;
		CHECK_ERR( c_batch.Create( aAttribs, _pVB->GetBufferId(), 0, 0, 0, gl_primitive::TRIANGLE, gl_index::_UNKNOWN ) );
		c_batch._uVertexSize = uVertSize;

		uIndex = _aBatchTypes.Count();
		_aBatchTypes.PushBack( c_batch );
		return true;
	}

/*
=================================================
	удаляет батч рендер
=================================================
*/
	void GLBatchRender::_Destroy()
	{
		Clear();
		_aBatchTypes.Clear();
		if ( _pVB != null ) _pVB->Destroy();
	}
	
/*
=================================================
	проверяет данные
=================================================
*/
	inline bool GLBatchRender::IsValid() const
	{
		return ( !_aVertices.Empty() and !_aBatchTypes.Empty() and !_aBatches.Empty() );
	}

/*
=================================================
	выводит все батчи
=================================================
*/
	bool GLBatchRender::Flush()
	{
		if ( Empty() )
			return false;

		ASSERT( IsValid() );

		_UpdateBuffers();

		FOR( i, _aBatches )
		{
			const TBatch &	batch	= _aBatches[i];
			GLBatch &		c_batch	= _aBatchTypes[ batch.uBatchType ];

			c_batch._uOffset	= batch.uOffset  / c_batch._uVertexSize;
			c_batch._uCount		= batch.uVerSize / c_batch._uVertexSize;
			c_batch._ePrimitive	= batch.eMode;

			batch.pDrawProc( batch.pParameter, c_batch );	// call c_batch.DrawArrays()
		}

		return true;
	}

/*
=================================================
	выводит uCount батчей начиная с uFirst
=================================================
*/
	bool GLBatchRender::FlushRange(usize uFirst, usize uCount)
	{
		if ( Empty() )
			return false;

		ASSERT( IsValid() );
		
		_UpdateBuffers();

		uCount += uFirst;

		if ( uCount > _aBatches.Count() )
			uCount = _aBatches.Count();

		for (usize i = uFirst; i < uCount; ++i)
		{
			const TBatch &	batch	= _aBatches[i];
			GLBatch &		c_batch	= _aBatchTypes[ batch.uBatchType ];
			
			c_batch._uOffset	= batch.uOffset;
			c_batch._uCount		= batch.uVerSize / c_batch._uVertexSize;
			c_batch._ePrimitive	= batch.eMode;

			batch.pDrawProc( batch.pParameter, c_batch );	// call c_batch.DrawArrays()
		}

		return true;
	}
	
/*
=================================================
	записывает данные в буфер
=================================================
*/
	inline void GLBatchRender::_UpdateBuffers()
	{
		if ( !_bChanged )
			return;
		
		_bChanged  = false;
		_pVB->SetData( _aVertices.ptr(), _aVertices.Size() );
	}
	
/*
=================================================
	сортирует батчи по глубине
	от дальних к ближним
=================================================
*/
	inline void GLBatchRender::_ZSort(usize uFirst, usize uCount)
	{
		if ( uFirst >= _aBatches.Count() )
			return;

		if ( uFirst + uCount > _aBatches.Count() )
			uCount = _aBatches.Count() - uFirst;

		usize				j	= 0;
		float				f_z = 0.f;
		array< TBatch >		a_temp;
		array< float >		a_cache;

		a_temp.Reserve( uCount );
		a_cache.Reserve( uCount );

		for (usize i = uFirst; i < uCount; ++i)
		{
			f_z = _aBatches[i].fDepth;

			for (j = 0;; ++j)
			{
				if ( j < a_cache.Count() )
				{
					a_temp.PushBack( _aBatches[i] );
					a_cache.PushBack( f_z );
					break;
				}
				if ( f_z < a_cache[j] )
				{
					a_temp.Insert( _aBatches[i], j );
					a_cache.Insert( f_z, j );
					break;
				}
			}
		}
	}
	
/*
=================================================
	удаляет батч из списка
=================================================
*/
	bool GLBatchRender::RemoveBatch(usize uNumb)
	{
		if ( uNumb >= _aBatches.Count() )
			return false;

		_aBatches.Erase( uNumb );
		return true;
	}

/*
=================================================
	удаляет батч из списка
=================================================
*/
	bool GLBatchRender::RemoveBatch(void *pParam, PDrawBatchProc_t pDraw, gl_primitive::type eMode, usize uBatchTypeIndex)
	{
		if ( pDraw == null )
			return false;

		FOR( i, _aBatches )
		{
			if ( _aBatches[i].pParameter == pParam and
				 _aBatches[i].pDrawProc == pDraw and
				 _aBatches[i].eMode == eMode  and
				 _aBatches[i].uBatchType == uBatchTypeIndex )
			{
				_aBatches.Erase( i );
				return true;
			}
		}
		return false;
	}

/*
=================================================
	удаляет все батчи
=================================================
*/
	void GLBatchRender::Clear()
	{
		_aVertices.SetCount(0);
		_aBatches.ClearMem();

		_fCurrentZ	= _fDefZ;
		_bChanged	= true;
	}

//-------------------------------------------------------------------

}	// GLShell
}	// UX_STL